﻿using System;
using System.Collections.Generic;
using System.Linq;
using Common.Extensions;
using Contact.DAL.Models.Worktime;
using EFDataModel;
using TimeManagement;

namespace ContactMvc4TB.Models.Worktime.Helpers
{
    /// <summary>
    /// Dient der Berechnung des letzten Tages für den noch kein Arbeitszeiteintrag angelegt wurde.
    /// </summary>
    public class CalculateNoWorktimeEntryDays
    {
        #region Member
        private long UserId { get; set; }
        private EFWorktimeSetting WorktimeSetting { get; set; }
        #endregion

        #region Public Functions
        public CalculateNoWorktimeEntryDays(long userId)
        {
            UserId = userId;
            //Die Worktimeeinstellungen für den User laden um zu ermitteln, wann der User arbeiten muss.
            WorktimeSettingsRepository settingsRepository = new WorktimeSettingsRepository(CurrentHttpContext.GetDataModel());
            WorktimeSetting = settingsRepository.LoadWorktimeSetting(UserId);
        }

        /// <summary>
        /// Gibt für das aktuelle Jahr, den letzten nicht eingetragenen Arbeitstag zurück.
        /// </summary>
        public DateTime GetLastNoWorktimeEntryDayCurrentYear()
        {
            DateTime startDate = DateTime.Now.FirstDayOfYear();
            //Wenn es sich beim Startdatum um das aktuelle Jahr handelt, dann erst ab dem passenden Datum suchen.
            //Hier wird das Datum genommen, an dem sich die Arbeitszeiteinträge das letzte mal geändert haben.
            if (WorktimeSetting.WorkingDaysChangedDate.Year == DateTime.Now.Year)
            {
                startDate = WorktimeSetting.WorkingDaysChangedDate;
            }

            WorktimeRepository worktimeRepository = new WorktimeRepository(CurrentHttpContext.GetDataModel());
            List<EFWorktime> worktimes = worktimeRepository.GetWorktime(UserId, startDate, DateTime.Now).ToList();
            //Als Standardwert den nächsten Arbeitstag heraussuchen
            DateTime noEntryFound = WorktimeSetting.GetNextWorkingDay(DateTime.Now.AddDays(-1));
            //Alle Tage durchgehen und den ältesten nicht eingetragebenen Arbeitstag ermitteln.
            foreach (DateTime date in CreateDates(startDate))
            {
                //Wenn der Tag zu einem Arbeitstag gehört, dann prüfen ob dieser in unserer Arbeitszeitliste eingetragen wurde.
                if (WorktimeSetting.HasWorkingDaySelected(date))
                {
                    var work = worktimes.FirstOrDefault(p => p.EntryDate == date);
                    if (work == null && date < noEntryFound)
                    {
                        noEntryFound = date;
                    }
                }
            }

            return noEntryFound;
        }

        /// <summary>
        /// Zählt die Anzahl der noch offenen Arbeitstage die eingetragen werden müssen. Hier wird wieder nur ab dem 
        /// WorkingDaysChangedDate gerechnet.
        /// </summary>
        public int CountNoWorktimeEntryDays()
        {
            DateTime startDate = DateTime.Now.FirstDayOfYear();
            if (WorktimeSetting.WorkingDaysChangedDate.Year == DateTime.Now.Year)
            {
                startDate = WorktimeSetting.WorkingDaysChangedDate;
            }

            WorktimeRepository worktimeRepository = new WorktimeRepository(CurrentHttpContext.GetDataModel());
            List<EFWorktime> worktimes = worktimeRepository.GetWorktime(UserId, startDate, DateTime.Now).ToList();
            int count = 0;

            //Alle Tage durchgehen und den ältesten nicht eingetragebenen Arbeitstag ermitteln.
            foreach (DateTime date in CreateDates(startDate))
            {
                //Wenn der Tag zu einem Arbeitstag gehört, dann prüfen ob dieser in unserer Arbeitszeitliste eingetragen wurde.
                if (WorktimeSetting.HasWorkingDaySelected(date))
                {
                    //Prüfen ob wir den Arbeitstag bereits eingetragen haben. Wenn nicht dann passend hochzählen
                    var work = worktimes.FirstOrDefault(p => p.EntryDate == date);
                    if (work == null)
                    {
                        count++;
                    }
                }
            }

            return count;
        }
        #endregion

        #region private functions
        private List<DateTime> CreateDates(DateTime startdate)
        {
            List<DateTime> dates = new List<DateTime>();
            //Die Zeitspanne für das Start und endedatum ermitteln
            TimeSpan timeStart = new TimeSpan(startdate.Ticks);
            TimeSpan timeEnd = new TimeSpan(DateTime.Now.Ticks);
            //Anhand der Differenz die Anzahl der Tage ermitteln die dazwischen liegt.
            TimeSpan diff = timeEnd.Subtract(timeStart);

            //Alle Tage durchgehen und unserer Liste hinzufügen.
            for (int i = 1; i < diff.TotalDays; i++)
            {
                dates.Add(startdate.Date);
                startdate = startdate.AddDays(1);
            }

            return dates;
        }
        #endregion
    }
}